<?php

/**
 * A representation of two adjacent joined tables in ORM.
 * This class doesn't "do" anything other than provide details.
 * @package Nina
 * @author Chris Corbyn
 */
class Nina_Orm_Join
{
	/*! Join types available */
	const INNER = "INNER";
	const LEFT = "LEFT";
	const LEFT_OUTER = "LEFT OUTER";
	const RIGHT = "RIGHT";
	const RIGHT_OUTER = "RIGHT OUTER";
	
	/**
	 * The class name of the left table.
	 * @var string
	 */
	protected $leftTableClass;
	/**
	 * The table at the left hand side of the join.
	 * @var string
	 */
	protected $leftTable;
	/**
	 * The field at the left hand side of the join.
	 * @var string
	 */
	protected $leftField;
	/**
	 * The class name of the right table.
	 * @var string
	 */
	protected $rightTableClass;
	/**
	 * The table at the right hand side of the join.
	 * @var string
	 */
	protected $rightTable;
	/**
	 * The field at the right hand side of the join.
	 * @var string
	 */
	protected $rightField;
	/**
	 * The type of join (as in INNER, LEFT etc)
	 * @var string
	 */
	protected $type;
	
	/**
	 * Constructor
	 * Sets up this join. You do have to use the constructor from the outside, rather than any accessors.
	 * @param array|string [$left=null] The left hand side of the join as (ClassName, ConstantName)
	 * @param array|string [$right=null] The right hand side of the join as (ClassName, ConstantName)
	 * @param string [$type=self::INNER] The type of join (INNER etc)
	 * @throws Nina_Exception_Orm If bad/invalid mapping is used.
	 */
	public function __construct($left = null, $right = null, $type = self::INNER) {
		if ($left !== null) {
			if(is_array($left))
				$left = array_values($left);
			else
				$left = explode('::', $left);

			if (count($left) != 2) {
				throw new Nina_Exception_Orm("Parameter 1 in Nina_Orm_Join::__construct() must have two elements or be a table and column definition.");
			}
			
			$this->setLeft($left[0], $left[1]);
		}
		if ($right !== null) {
			if(is_array($right))
				$right = array_values($right);
			else
				$right = explode('::', $right);

			if (count($right) != 2) {
				throw new Nina_Exception_Orm("Parameter 2 in Nina_Orm_Join::__construct() must have two elements or be a table and column definition.");
			}
			$this->setRight($right[0], $right[1]);
		}
		$this->setType($type);
	}

	/**
	 * Set the left hand side of the join.
	 * @param string $class Class name
	 * @param string $field Constant name
	 * @throws Nina_Exception_Orm If invalid arguments are passed
	 */
	protected function setLeft($class, $field) {
		if (!Nina_Orm::isTable($class)) {
		  throw new Nina_Exception_Orm("Unable to join ORM table " . $class . ". The class can't be found.");
		}
		if (!Nina_Orm::isField($class, $field)) {
		  throw new Nina_Exception_Orm("Unable to join ORM table " . $class .
			". Check the mappings is correct (const " . $class . "::" . $field . " not found).");
		}

		$this->leftTableClass = $class;

		$table = new $class();
		$this->leftTable = $table->getTableName();
		$this->leftField = constant($class . "::" . $field);
	}

	/**
	 * Set the right hand side of the join.
	 * @param string $class Class name
	 * @param string $field Constant name
	 * @throws Nina_Exception_Orm If invalid arguments are passed
	 */
	protected function setRight($class, $field) {
		if (!Nina_Orm::isTable($class)) {
		  throw new Nina_Exception_Orm("Unable to join ORM table " . $class . ". The class can't be found.");
		}
		if (!Nina_Orm::isField($class, $field)) {
		  throw new Nina_Exception_Orm("Unable to join ORM table " . $class .
			". Check the mappings is correct (const " . $class . "::" . $field . " not found).");
		}

		$this->rightTableClass = $class;

		$table = new $class();
		$this->rightTable = $table->getTableName();
		$this->rightField = constant($class . "::" . $field);
	}

	/**
	 * Set the type of Join (INNER, LEFT etc).
	 * This can be adjusted after the object has been initialized.
	 * @param string $type Type
	 */
	public function setType($type) {
		$this->type = $type;
	}

	/**
	 * Get the type of join.
	 * @return string
	 */
	public function getType() {
		return $this->type;
	}

	/**
	 * Get the class name of the left table.
	 * @return string
	 */
	public function getLeftTableClass() {
		return $this->leftTableClass;
	}

	/**
	 * Get the class name of the right table.
	 * @return string
	 */
	public function getRightTableClass() {
		return $this->rightTableClass;
	}

	/**
	 * Get the table name at the left of the join.
	 * This returns the real table name, not the class representing it.
	 * @return string
	 */
	public function getLeftTable() {
		return $this->leftTable;
	}

	/**
	 * Get the table name at the right of the join.
	 * This returns the real table name, not the class representing it.
	 * @return string
	 */
	public function getRightTable() {
		return $this->rightTable;
	}

	/**
	 * Get the field at the left of the join.
	 * This returns the real field name, not the constant representing it.
	 * @return string
	 */
	public function getLeftField() {
		return $this->leftField;
	}

	/**
	 * Get the field at the right of the join.
	 * This returns the real field name, not the constant representing it.
	 * @return string
	 */
	public function getRightField() {
		return $this->rightField;
	}
}
